package de.gameduell.framework.gui.scroll {
	import flash.display.DisplayObject;
	import flash.display.Sprite;
	import flash.events.Event;
	import flash.geom.Point;

	/**
	 * ...
	 * @author Frederik Schricker - frederik.schricker@gameduell.de
	 */
	public class CoverFlow extends Sprite
	{
		private var _container:Sprite;
		private var _items:Array;
		private var _width:uint;
		private var _focalLength:uint = 250;
		private var _viewPoint:Point;
		private var _speed:uint;
		private var _currentPositionIndex:uint = 0;//@TODO initial value
		private var _currentMainItemIndex:uint = 0;
		private var _configArray:Array;
		private var _offsetX:Number;
		private var _autoSortZ:Boolean;
		
		public function CoverFlow(container:Sprite, items:Array, width:uint, configArr:Array, speed:uint, vp:Point, offsetX:Number = 0, autoSortZ:Boolean = true) {
			_autoSortZ = autoSortZ;
			_offsetX = offsetX;
			_viewPoint = vp;
			_speed = speed;
			_configArray = configArr;
			_width = width;
			_container = container;
			generateItems(items);
			if (container.parent == null) {
				_container.addEventListener(Event.ADDED_TO_STAGE, init);
			} else {
				init();
			}
		}
		
		//public
		
		public function move(steps:int = 1):void {
			if (steps == 0) {
				return;
			}
			_currentPositionIndex -= steps;
			var moveDistance:Number = steps * _width;
			for (var i:uint = 0; i < _items.length; i++) {
				(_items[i] as Sprite3D).targetXPos += moveDistance;
			}
		}
		
		public function moveTo(index:uint):void {
			if (index < 0 || index > _items.length - 1 || index == _currentPositionIndex) {
				return;
			}
			_currentPositionIndex = index;
			for (var i:uint = 0; i < _items.length; i++) {
				(_items[i] as Sprite3D).targetXPos =  (i - index + _offsetX) * _width;
			}
		}
		
		public function setZIndex(itemId:uint, index:uint) : void {
			if(_autoSortZ) {
				return;
			}
			_container.setChildIndex(_items[itemId], index);
		}

		//private
		
		private function init(e:Event = null):void {
			generateGraphics();
			addEventListener(Event.ENTER_FRAME, onEnterFrame);
		}
		
		
		private function generateItems(items:Array):void {
			_items = [];
			for (var i:uint = 0; i < items.length; i++) {
				_items.push(new Sprite3D(items[i] as DisplayObject, (i + _offsetX) * _width));
			}
		}
		
		private function generateGraphics():void {
			for (var i:uint = 0; i < _items.length; i++) {
				var currentItem:Sprite3D = _items[i] as Sprite3D;
				_container.addChild(currentItem); 
				currentItem.xPos = (i + _offsetX) * _width;
			}
		}
		
		private function getRelativePositions():void {
			for (var i:uint = 0; i < _items.length; i++) {
				var currentItem:Sprite3D = _items[i] as Sprite3D;
				if (i >= 0 && i < _currentPositionIndex && Math.abs(_currentPositionIndex - i) < _configArray.length) {
					currentItem.relativeXPosition = Sprite3D.REL_POS_LEFT;
				} else if (i < _items.length && i > _currentPositionIndex && Math.abs(_currentPositionIndex - i) < _configArray.length) {
					currentItem.relativeXPosition = Sprite3D.REL_POS_RIGHT;
				} else if (i == _currentPositionIndex) {
					currentItem.relativeXPosition = Sprite3D.REL_POS_IS_MAINITEM;
				} else {
					currentItem.relativeXPosition = Sprite3D.REL_POS_DEFAULT;
				}
			}
		}
		
		private function applyConfig3D():void {
			var leftItemsConfigIndex:uint = 0;
			var rightItemsConfigIndex:uint = 0;
			for (var t:uint = 0; t < _items.length; t++) {
				var currentItem:Sprite3D = _items[t] as Sprite3D;
				switch(currentItem.relativeXPosition) {
					case Sprite3D.REL_POS_DEFAULT:
					currentItem.filters = [];
					currentItem.targetYPos = currentItem.targetZPos = 0;
					break;
					case Sprite3D.REL_POS_IS_MAINITEM:
					currentItem.targetYPos = _configArray[_configArray.length - 1].y;
					currentItem.targetZPos = _configArray[_configArray.length - 1].z;
					trace("currentmainitem_x: " + currentItem.xPos);
					break;
					case Sprite3D.REL_POS_LEFT:
					currentItem.targetYPos = _configArray[leftItemsConfigIndex].y;
					currentItem.targetZPos = _configArray[leftItemsConfigIndex].z;
					leftItemsConfigIndex++;
					break;
					case Sprite3D.REL_POS_RIGHT:
					currentItem.targetYPos = _configArray[_configArray.length - 2 - rightItemsConfigIndex].y;
					currentItem.targetZPos = _configArray[_configArray.length - 2 - rightItemsConfigIndex].z;
					rightItemsConfigIndex++;
					break;
				}
			}
		}
		
		private function sortZ(prop:String = "zPos"):void {
			var sortArr:Array = _items.concat([]);
			sortArr.sortOn(prop, Array.DESCENDING | Array.NUMERIC);
			for (var i:uint = 0; i < sortArr.length; i++) {
				var currentItem:Sprite3D = sortArr[i] as Sprite3D;
				_container.setChildIndex(currentItem, i);
			}
		}
		
		private function updatePosition(item:Sprite3D, axe:String ):void {
			var currentPosition : Number = item[axe.toLowerCase() + "Pos"];
			var targetPosition : Number = item["target" + axe.toUpperCase() + "Pos"];
			var moveDir:int = currentPosition < targetPosition ? _speed : -_speed;
			if ((currentPosition < targetPosition && moveDir > 0) || (currentPosition > targetPosition && moveDir < 0) ) {
				item[axe.toLowerCase() + "Pos"] += moveDir;
			} else {
				item[axe.toLowerCase() + "Pos"] = targetPosition;
			}
		}
		
		//getter/setter
		
		public function set currentMainItemIndex(value:uint):void {
			_currentMainItemIndex = value;
		}
		
		public function get currentMainItemIndex():uint {
			return _currentMainItemIndex;
		}

		//handler
		
		private function onEnterFrame(e:Event):void { 
			//_viewPoint.x = _container.stage.stageWidth / 2 + (_container.stage.stageWidth / 2 - mouseX);
			//_viewPoint.y = _container.stage.stageHeight / 2  + (_container.stage.stageHeight / 2 - mouseY);
			if(_autoSortZ) {
				sortZ();
			}
			if(_configArray.length > 0) {
				getRelativePositions();
				applyConfig3D();
			}
			for (var i:uint = 0; i < _items.length; i++) {
				var currentItem:Sprite3D = _items[i] as Sprite3D;
				//updatePosition(currentItem, "x");
				//updatePosition(currentItem, "y");
				//updatePosition(currentItem, "z");
				var scale:Number = _focalLength / (_focalLength + currentItem.zPos);
				currentItem.scaleX = currentItem.scaleY = scale;
				currentItem.x = (_viewPoint.x + currentItem.xPos) * scale;
				currentItem.y = (_viewPoint.y + currentItem.yPos) * scale;
			}
		}
	}
}

import fl.transitions.easing.Regular;

import de.gameduell.framework.gui.animation.GDTween;

import flash.display.DisplayObject;
import flash.display.Sprite;

internal class Sprite3D extends Sprite {
	
	private var _dpo:DisplayObject;
	
	public var xPos:Number = 0;
	public var yPos:Number = 0;
	public var zPos:Number = 0;
	private var _targetXPos:Number = 0;
	private var _targetYPos:Number = 0;
	private var _targetZPos:Number = 0;
	private var _tweenX:GDTween;
	private var _tweenY:GDTween;
	private var _tweenZ:GDTween;
	
	private const TWEEN_TIME:Number = .5;
	public var relativeXPosition:String = REL_POS_DEFAULT;
	
	
	public static const REL_POS_DEFAULT:String = "relposdefault";
	public static const REL_POS_LEFT:String = "relposleft";
	public static const REL_POS_IS_MAINITEM:String = "relposismainitem";
	public static const REL_POS_RIGHT:String = "relposright";
	
	public function Sprite3D(dpo:DisplayObject, txp:Number) {
		_targetXPos = txp;
		addChild(dpo);
		dpo.x = - dpo.width / 2;
		dpo.y = - dpo.height / 2;
		_dpo = dpo;
	}
	
	public function set targetXPos(value:Number):void {
		_targetXPos = value;
		_tweenX = new GDTween(this, "xPos", Regular.easeIn, xPos, _targetXPos, TWEEN_TIME, true);
		_tweenX.start();
	}
	
	public function set targetYPos(value:Number):void {
		_targetYPos = value;
		_tweenY = new GDTween(this, "yPos", Regular.easeIn, yPos, _targetYPos, TWEEN_TIME, true);
		_tweenY.start();
	}
	
	public function set targetZPos(value:Number):void {
		_targetZPos = value;
		_tweenZ = new GDTween(this, "zPos", Regular.easeIn, zPos, _targetZPos, TWEEN_TIME, true);
		_tweenZ.start();
	}
	
	public function get targetXPos():Number{
		return _targetXPos;
	}
	
	public function get targetYPos():Number{
		return _targetYPos;
	}
	
	public function get targetZPos():Number{
		return _targetZPos;
	}
}
